/*
 * Mundo.cpp
 *
 *  Created on: 06/10/2013
 *      Author: Pablo
 */

#include "Mundo.h"
#include <Box2D/Box2D.h>
#include <iostream>
#include "../modelo/CreadorElementosFisicos.h"
#include "CollitionListener.h"
#include "../modelo/Tijera.h"
#include "PoleaFisica.h"
#include "TijeraFisica.h"
#include "../modelo/Polea.h"


using namespace std;


Mundo::Mundo(Ventana* unaVentana) {

	mundo = NULL;
	ventana = unaVentana;

}

void Mundo::inicializar(){

	if (mundo != NULL){
		delete mundo;
		cout << "El mundo de entrada NO estaba vacio " << endl;
	} else {
		cout << "El mundo de entrada estaba vacio " << endl;
	}

	// Inicializo el mundo
	b2Vec2 gravedad(0.0f, -10.0f);
	mundo = new b2World(gravedad);

	b2BodyDef groundBodyDef;
	groundBodyDef.position.Set(0.0f, -10.0f);


	// llama al creador de cuerpos, pasandole su definicion
	//ademas crea un objeto para el cuerpo del piso.
	b2Body* groundBody = mundo->CreateBody(&groundBodyDef);

	// Define el cuadrado que representa al piso
	b2PolygonShape groundBox;

	// setea los valores que van a ser de 100 de ancho, por 20 de alto.
	groundBox.SetAsBox(100.0f, 10.0f);

	// agregar el fixture al body del piso ( el fixture son las propiedades fisicas
	// propias de ese cuerpo, pero como es el piso no tiene ninguna.)

	groundBody->CreateFixture(&groundBox, 0.0f);


	list<Elemento*>* listaElementos = ventana->getCanvas()->getListaElementos();
	CreadorElementosFisicos *creadorElementos = new CreadorElementosFisicos(listaElementos);
	creadorElementos->obtenerElemento(this);
	creadorElementos->jointearElementos(this);

	//listener: se fija si hay colisiones, y si las hay se fija que es lo que colisiona y
	// hace lo que tenga uqe hacer para ese caso.
	this->collitionListener = new CollitionListener();
	this->mundo->SetContactListener(collitionListener);
}

Mundo::~Mundo() {
	// TODO Auto-generated destructor stub
}

//b2Body* Mundo::crearPelota(float32 posX, float32 posY, float32 radio,float32 densidad, float32 restitucion){
//
//	//tiro la definicion del cuerpo pelota,que es dinamico, y lo pongo en 0,0 (por ahora).
//	b2BodyDef bodyDefPelota;
//	bodyDefPelota.type = b2_dynamicBody;
//
//	bodyDefPelota.position.Set(posX, posY);
//
//	b2Body* pelota = this->mundo->CreateBody(&bodyDefPelota);
//
//	//creo la forma de la pelota mediante un shape
//	b2CircleShape circulo;
//	circulo.m_radius = radio;
//
//	//definiciones Fisicas de la pelota
//
//	b2FixtureDef fd;
//	fd.shape = &circulo;
//	fd.density =densidad;
//	//coeficiente de restitucion (cuanto se conserva la energia cinetica?).
//	fd.restitution = restitucion;
//
//	pelota->CreateFixture(&fd);
//
//	//this->cuerpoFisico = world.CreateBody(&(*definicionCuerpoFisico));
//
//	return pelota;
//
//}
//
//b2Body* Mundo::crearGlobo(float32 posX, float32 posY, float32 radio, float32 densidad){
//
//	b2BodyDef bodyDefGlobo;
//	bodyDefGlobo.type = b2_dynamicBody;
//	//bodyDefGlobo.position.Set(0.0f, 1.0f);
//	bodyDefGlobo.position.Set(posX, posY);
//	b2Body* globo = mundo->CreateBody(&bodyDefGlobo);
//
//	b2CircleShape circulo;
//	circulo.m_radius = radio;//1.5f;
//
//	b2FixtureDef fd;
//	fd.shape = &circulo;
//	fd.density = densidad;//1.0f;
//	globo->CreateFixture(&fd);
//	return globo;
//
//}
//
//b2Body* Mundo::crearBolaBowling(float32 posX, float32 posY, float32 radio, float32 densidad, float32 friccion, float32 restitucion){
//
//	b2BodyDef bodyDefBowling;
//	bodyDefBowling.type = b2_dynamicBody;
////	bodyDefBowling.position.Set(6.0f, 3.0f);
//	bodyDefBowling.position.Set(posX, posY);
//
//	b2Body* bolaBowling = mundo->CreateBody(&bodyDefBowling);
//
//	b2CircleShape circulo;
//	//circulo.m_radius = 1.5f;
//	circulo.m_radius = radio;
//	b2FixtureDef fd;
//	fd.shape = &circulo;
//	fd.density = densidad;//1.0f;
//	fd.restitution = restitucion;//0.1f;
//	fd.friction = friccion;//0.3f;
//
//	bolaBowling->CreateFixture(&fd);
//
//	return bolaBowling;
//}
//
//b2Body* Mundo::crearViga(float32 tamanio, float32 angulo, float32 posX, float32 posY, float32 densidad){
//
//	b2BodyDef bodyDefViga;
//	//bodyDefViga.position.Set(6.0f, 10.0f);
//	bodyDefViga.position.Set(posX, posY);
//
//	bodyDefViga.angle = angulo;
//	b2Body* viga = mundo->CreateBody(&bodyDefViga);
//
//	b2PolygonShape rectangulo;
//	//seteo las dimensiones de la viga, que son 28 x 0.6.
//	rectangulo.SetAsBox(tamanio/2, 0.3f);
//
//	b2FixtureDef fd;
//	fd.shape = &rectangulo;
//	fd.density = densidad;// 0.0f;
//
//	viga->CreateFixture(&fd);
//
//	return viga;
//}

b2World* Mundo::getWorld(){
	return this->mundo;
}


void Mundo::simulateStep(){

	float32 timeStep = 3.0f / 60.0f;
	int32 velocityIterations = 6;
	int32 positionIterations = 2;

//	Elemento *engranaje = ventana->getCanvas()->getListaElementos()->back();
//	cout << "POSICIONES FISICAS ENGRANAJE" << endl;
//		cout << "ANTES " << endl;
//		cout << " X: (" <<
//				engranaje->getEntidadFisica()->obtenerPosicionX()  << ", " <<
//				engranaje->getEntidadFisica()->obtenerPosicionY() << "). " <<
//				" ALTO:" << engranaje->getAlto() << endl;


	mundo->Step(timeStep, velocityIterations, positionIterations);

	//cout << "Acabo de simular " << endl;

	list<Elemento*>::iterator iteradorElementos = ventana->getCanvas()->getListaElementos()->begin();
//cout<<"Mundo.cpp : Creo el iterador"<<endl;

//	cout << "SIMULACION" << endl;

	while (iteradorElementos != ventana->getCanvas()->getListaElementos()->end()) {
		Elemento * elemento = (*iteradorElementos);

		elemento->actualizarValoresFisicos();
		elemento->getEntidadFisica()->aplicarFuerzasFisicas();

		if (elemento->getTipo().compare("atijera")==0){
			this->detectarCortesTijera(ventana->getCanvas()->getListaElementos(), elemento);
		}
		if(elemento->getTipo().compare("engranaje")==0){
			Engranaje* engranaje = (Engranaje*) elemento;
			if(elemento->getEntidadFisica()->obtenerCuerpo()->GetAngularVelocity()!=0) {
				engranaje->estaAndando = true;
			}else{
				engranaje->estaAndando = false;
			}
		}

		iteradorElementos++;
	}


	this->destruirObjetosEliminados();

}


void Mundo::destruirObjetosEliminados(){
	list<Elemento*>::iterator iteradorElementos = ventana->getCanvas()->getListaElementos()->begin();

	while (iteradorElementos != ventana->getCanvas()->getListaElementos()->end()) {
		Elemento * elemento = (*iteradorElementos);
		EntidadFisica* entidad = elemento->getEntidadFisica();
		if (entidad->estaEliminado()){
			entidad->destruirEntidadFisica();

			Elemento* elementoABorrar = (*iteradorElementos);
			++iteradorElementos;
			if (elementoABorrar->getTipo().compare("zpolea")!=0){
				ventana->getCanvas()->getListaElementos()->remove(elementoABorrar);
				delete entidad;
				delete elementoABorrar;
			}
		}
		else{
			++iteradorElementos;
		}
	}
}


void Mundo::detectarCortesTijera(list<Elemento*>* listaElementos, Elemento* tijera){

	list<Elemento*>::iterator iterador = listaElementos->begin();
	Tijera* unaTijera = (Tijera*) tijera;

	for(; iterador != listaElementos->end(); iterador++){
		Elemento* elemento = (*iterador);

		if (elemento->getTipo().compare("soga")==0){

			pair<double,double> posicionInicial = elemento->getPosicionInicial();
			pair<double,double> posicionFinal = elemento->getPosicionFinal();
			pair<double,double> posicionCentroTijera = unaTijera->getCentroCorte();

			double anguloEntreSogas = atan2(posicionFinal.second - posicionInicial.second,
					posicionFinal.first - posicionInicial.first);
		/*	cout << "Angulo formado por las sogas: " << anguloEntreSogas*180/3.1416<< endl;
			cout <<"CENTRO CORTE TIJERA X: " << posicionCentroTijera.first << " Y: "<<
					posicionCentroTijera.second<< endl;*/
				double anguloTijeraSoga = atan2(posicionCentroTijera.second - posicionInicial.second,
					posicionCentroTijera.first - posicionInicial.first);
		/*	cout << "Angulo formado entre la tijera y la soga " <<
					elemento->getIdElemento() << " : " << anguloTijeraSoga*180/3.1416<< endl;
*/
			// Saco el recuadro que forman las sogas
			double xMax,xMin,yMax,yMin;

			if (posicionFinal.first > posicionInicial.first){
				xMax = posicionFinal.first;
				xMin = posicionInicial.first;
			} else {
				xMax = posicionInicial.first;
				xMin = posicionFinal.first;
			}

			if (posicionFinal.second > posicionInicial.second){
				yMax = posicionFinal.second;
				yMin = posicionInicial.second;
			} else {
				yMax = posicionInicial.second;
				yMin = posicionFinal.second;
			}

			// Le doy cierta tolerancia a los margenes
			xMin = xMin - 2;
			yMin = yMin - 2;
			xMax = xMax + 2;
			yMax = yMax + 2;

			if (posicionCentroTijera.first >= xMin && posicionCentroTijera.first <= xMax
					&& posicionCentroTijera.second >= yMin && posicionCentroTijera.second <= yMax) {
			//	cout << "El centro de corte cayo entre las sogas "<< endl;
				if (abs(abs(anguloEntreSogas*180/3.1416)- abs(anguloTijeraSoga*180/3.1416)) < 5
						&& unaTijera->estaCerrada()){
				//	cout << "Tengo que cortar la soga con ID: " << elemento->getIdElemento() << endl;

					TijeraFisica* tijerafisica = (TijeraFisica*)tijera->getEntidadFisica();

					if (tijerafisica->tijeraEstaCerrada()){
						eliminarRecursivo(elemento);
				//		cout<<"DETERMINO ELEMENTOS DE LOS COSTADOS DE LA SOGA : "<<elemento->obtenerExtremos().first->getTipo()<<" "<<elemento->obtenerExtremos().second->getTipo()<<endl;
					}

				}
			}
		}
	}
}



void Mundo::eliminarRecursivo(Elemento* aEliminar){
	if (!aEliminar->fueVisitadoEnEliminar()){
		if ((aEliminar->getTipo().compare("soga")==0) || (aEliminar->getTipo().compare("zpolea")==0)){
			cout<<"elimino: "<<aEliminar->getTipo()<<endl;
			aEliminar->getEntidadFisica()->eliminarEntidadFisica();
			aEliminar->setFueVisitado(true);
			Elemento* elementoIzquierdo = aEliminar->obtenerExtremos().first;
			Elemento* elementoDerecho = aEliminar->obtenerExtremos().second;
			eliminarRecursivo(elementoIzquierdo);
			eliminarRecursivo(elementoDerecho);
		}
	}
}
